home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Commun⁄Network / RevRdist Folder / RevRdist / RevRdist src / match.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-31  |  26.6 KB  |  1,077 lines  |  [TEXT/KAHL]

  1. /*
  2.  * match.c - routines for synchronizing client and server folders,
  3.  *    based on the configuration file
  4.  */
  5.  
  6. #include "RevRdist.h"
  7. #include "dispatch.h"
  8. #include <AppleTalk.h>
  9. #include "TransSkelProto.h"
  10. #include "TransDisplayProto.h"
  11.  
  12. #define    fIsAlias    0x8000            /* finder alias flag */
  13.  
  14. /*
  15.  * define some bit pattern mnemonics indicating where a file/folder
  16.  * name appears
  17.  */
  18.  
  19. #define    xxD            1                /* entry is in control file tree */
  20. #define xSx            2                /* entry is in server catalog */
  21. #define Cxx            4                /* entry is in client catalog */
  22. #define    xSD            (xSx|xxD)        /* and various combinations */
  23. #define CxD            (Cxx|xxD)
  24. #define CSx            (Cxx|xSx)
  25. #define    CSD            (Cxx|xSx|xxD)
  26.  
  27. /*
  28.  * prototypes for local routines
  29.  */
  30. static tnode_t * checkTypes (dnode_t *, cnode_t *);
  31. static short    least (StringPtr, StringPtr, StringPtr);
  32. static void        mergeActions (actions_t *, actions_t *, actions_t *);
  33. static void        updateFInfo (cnode_t *, cnode_t *, short,
  34.                         action_t, action_t, action_t);
  35.  
  36.  
  37. /*
  38.  *=========================================================================
  39.  * matchFolder - synchronize one folder given in the control file tree
  40.  * entry:    first arg = ptr to dnode_t of folder to work on
  41.  *            second arg = ptr to default actions to apply
  42.  *            third arg = ptr to server dirID of folder equivalent to first arg
  43.  *            fourth arg = ptr to client dirID of ditto
  44.  *            fifth arg = ptr to boolean true if within junk folder
  45.  *=========================================================================
  46.  */
  47. DISPATCHED (matchFolder)
  48. {
  49.     struct lm                        /* local memory */
  50.     {
  51.         frame_t            f;            /* basic dispatch frame */
  52.         cnode_t *        clientp;    /* current client entry */
  53.         cnode_t *        serverp;    /* current server entry */
  54.         dnode_t *        distp;        /* current control file entry */
  55.         dnode_t *        dt;            /* copy of first arg */
  56.         cnode_t *        ct;            /* client catalog tree */
  57.         cnode_t *        st;            /* server catalog tree */
  58.         Longint            cd;            /* copy of fourth arg */
  59.         Longint            sd;            /* copy of third arg */
  60.         action_t        todo;        /* choice for multi-state actions */
  61.         cnode_t            tcn;        /* dummy client catalog entry */
  62.         dnode_t            tdn;        /* dummy control file entry */
  63.         cnode_t            tsn;        /* dummy server catalog entry */
  64.         StringPtr        name;        /* folder name */
  65.         actions_t        da;            /* default actions for folder */
  66.         Boolean            ij;            /* true if within junk folder */
  67.     };
  68.     typedef struct lm    lm_t;
  69.     register lm_t *        m;
  70.  
  71.     struct actions        ca;            /* current actions */
  72. register cnode_t *        clientp;    /* copy of lm.clientp */
  73.     StringPtr            cname;        /* client entry name */
  74.     ctype_t                ctype;        /* entry for file or folder */
  75.     dnode_t *            distp;        /* copy of lm.distp */
  76.     StringPtr            dname;        /* control file entry name */
  77.     OSErr                error;
  78.     action_t            forcedIC;    /* forced "ifClient" action */
  79.     int                    len;        /* string length temp */
  80.     StringPtr            name;        /* name of current entry */
  81.     Ptr                    paramv[5];    /* param for called dispatch routines */
  82.     short                result;        /* our return value */
  83.     cnode_t *            serverp;    /* copy of lm.serverp */
  84.     StringPtr            sname;        /* server entry name */
  85.     StringPtr            sp;            /* string temp */
  86.     Longint                temp;        /* temp dir ID */
  87.     action_t            todo;        /* action to perform */
  88.     tnode_t *            tp;            /* ptr to type_node matching file */
  89.     OSErr                uerror;        /* error while updating file/folder */
  90.     short                whereisit;    /* bits set per location of file/folder */
  91.     short                wherereal;    /* original copy of whereisit */
  92.  
  93.     error = 0;
  94.     uerror = 0;
  95.     result = request;
  96.     m = *(lm_t **)fh;
  97.     switch (request)
  98.     {
  99.     case R_INIT:
  100.         if (error = resizeFrame (fh, sizeof (lm_t)))
  101.             return R_ERROR;
  102.         m = *(lm_t **)fh;
  103.         m->dt = (dnode_t *)argv[0];
  104.         m->da = *((actions_t *)argv[1]);
  105.         m->sd = *(Longint *)argv[2];
  106.         m->cd = *(Longint *)argv[3];
  107.         m->ij = *(Boolean *)argv[4];
  108.         m->name = m->dt ? m->dt->name : "\p<unknown>";
  109.         m->f.state = 1;
  110.         return R_CONT;
  111.  
  112.     case R_BACKOUT:
  113.     case R_QUIT:
  114.         goto cleanexit;
  115.  
  116.     case R_CONT:
  117.         /*
  118.          * We handle only the first state here, in order to avoid nesting
  119.          * switches to an ugly depth.
  120.          * Build catalogs of the client and server folders.
  121.          */
  122.         if (m->f.state == 1)
  123.         {
  124.             if (Flags & PF_VERBOSE)
  125.                 notice (L_STARTF, m->name, nil);
  126.             if (m->cd)
  127.             {
  128.                 if ((m->ct = m->clientp = listFolder (ClientVol, m->cd)) == nil)
  129.                 {
  130.                     if (ClueID)
  131.                     {
  132.                         panic (false, E_SYS, nil);
  133.                         return R_BACKOUT;
  134.                     }
  135.                 }
  136.                 /*
  137.                  * Sense if we are starting on the junk folder
  138.                  */
  139.                 if (m->cd == File_list[FL_JUNK].f_info.dirID)
  140.                     m->ij = true;
  141.             }
  142.             if (m->sd)
  143.             {
  144.                 if ((m->st = m->serverp = listFolder (ServerVol, m->sd)) == nil)
  145.                 {
  146.                     if (ClueID == afpAccessDenied)
  147.                     {
  148.                         notice (L_ACCESS, m->name, nil);
  149.                     }
  150.                     else if (ClueID)
  151.                     {
  152.                         panic (false, E_SYS, nil);
  153.                         return R_BACKOUT;
  154.                     }
  155.                 }
  156.             }
  157.             if (m->dt && m->dt->childp)
  158.                 m->distp = m->dt->childp->sibp;
  159.             m->f.state = 2;
  160.             return result;
  161.         }
  162.         break;
  163.  
  164.     default:
  165.         return ( popCall (R_QUIT, nil) );    /* should not happen */
  166.     }
  167.  
  168.     /*
  169.      * Here is the bulk of the R_CONT code
  170.      * Note that if an action requires dispatching, this code is
  171.      * executed for each state.
  172.      */
  173.     clientp = m->clientp;
  174.     serverp = m->serverp;
  175.     distp = m->distp;
  176.     /*
  177.      * We are done when we reach the end of each of the three lists
  178.      */
  179.     if (!clientp && !serverp && !distp)
  180.     {
  181.         if (Flags & PF_VERBOSE)
  182.             notice (L_ENDF, m->name, nil);
  183.         else
  184.             statMsgClr ();
  185.         goto cleanexit;
  186.     }
  187.     /*
  188.      * Determine the next item to work on
  189.      */
  190.     cname = clientp ? clientp->name : HighValue;
  191.     sname = serverp ? serverp->name : HighValue;
  192.     dname = distp    ? distp->name    : HighValue;
  193.     wherereal = whereisit = least (cname, sname, dname);
  194.     if ((whereisit & xxD) && distp->altname)
  195.     {
  196.         /*
  197.          * If the file has an alternate name, use it after checking that
  198.          * the alternate file exists.
  199.          */
  200.         serverp = &m->tsn;
  201.         if (m->f.state == 2)
  202.         {
  203.             ZEROAT (serverp);
  204.             if (getInfo (distp->altname, ServerVol, m->sd, serverp))
  205.                 serverp->dirID = 0;    /* if altname doesn't exist */
  206.         }
  207.         if (serverp->dirID)
  208.             whereisit |= xSx;
  209.         else
  210.         {
  211.             whereisit &= ~xSx;
  212.             serverp = nil;
  213.         }
  214.     }
  215.     /*
  216.      * Select action list from control file entry if it exists,
  217.      * else from the defaults for this folder
  218.      */
  219.     if (whereisit & xxD)
  220.         mergeActions (&m->da, &distp->actions, &ca);
  221.     else
  222.         ca = m->da;
  223.     /*
  224.      * It might fall out that the client has the right name, but it is
  225.      * a file instead of a folder or vice versa.  Handle this by working
  226.      * with the client copy this pass and leaving the other copy for
  227.      * the next pass (forcing junking or discarding of the client copy).
  228.      */
  229.     forcedIC = 0;
  230.     if ((whereisit & xSD) == xSD && (dtype_t) serverp->ctype != distp->d_type)
  231.     {
  232.         /*
  233.          * Server does not match distfile
  234.          */
  235.         whereisit &= ~xxD;
  236.         wherereal &= ~xxD;
  237.         forcedIC = A_IGNORE;
  238.         sp = distp->altname ? distp->altname : dname;
  239.         notice (L_MISTYPE, sp, nil);
  240.         if (Flags & PF_BADSERVER)
  241.         {
  242.             panic (false, E_MISTYPE, sp, nil);
  243.             if (Quit)
  244.             {
  245.                 result = R_BACKOUT;
  246.                 goto cleanexit;
  247.             }
  248.         }
  249.     }
  250.     if ((whereisit & CSx) == CSx && clientp->ctype != serverp->ctype)
  251.     {
  252.         whereisit &= ~xSD;
  253.         wherereal &= ~xSD;
  254.         if (ca.ifclient != A_DISCARD && ca.ifserver == A_UPDATE
  255.         &&  forcedIC == 0)
  256.             forcedIC = A_JUNK;
  257.     }
  258.     if ((whereisit & CxD) == CxD && (dtype_t) clientp->ctype != distp->d_type)
  259.     {
  260.         whereisit &= ~xxD;
  261.         wherereal &= ~xxD;
  262.         if (ca.ifclient != A_DISCARD && ca.ifserver == A_UPDATE)
  263.             forcedIC = A_JUNK;
  264.     }
  265.     /*
  266.      * In a similar fashion, if a file exists on both client and server,
  267.      * but has a different type or creator, treat the files separately.
  268.      */
  269.     if ((whereisit & CSx) == CSx && clientp->ctype == C_FILE)
  270.         if (clientp->in.f.finfo.fdType != serverp->in.f.finfo.fdType
  271.          || clientp->in.f.finfo.fdCreator != serverp->in.f.finfo.fdCreator)
  272.         {
  273.             whereisit &= ~xSD;
  274.             wherereal &= ~xSD;
  275.             if (ca.ifclient != A_DISCARD && ca.ifserver == A_UPDATE)
  276.                 forcedIC = A_JUNK;
  277.         }
  278.     /*
  279.      * We know where the next file/folder is.  Fetch its name and type.
  280.      */
  281.     if (whereisit & xxD)
  282.     {
  283.         ctype = (ctype_t) distp->d_type;
  284.         name = distp->name;
  285.     }
  286.     else if (whereisit & xSx)
  287.     {
  288.         ctype = serverp->ctype;
  289.         name = serverp->name;
  290.         if (ctype == C_FILE && (tp = checkTypes (m->dt, serverp)))
  291.             mergeActions (&ca, &tp->actions, &ca);
  292.     }
  293.     else if (whereisit & Cxx)
  294.     {
  295.         ctype = clientp->ctype;
  296.         name = clientp->name;
  297.         if (ctype == C_FILE && (tp = checkTypes (m->dt, clientp)))
  298.             mergeActions (&ca, &tp->actions, &ca);
  299.     }
  300.     else
  301.     {
  302.         /*
  303.          * Should not happen
  304.          */
  305.         ctype = C_FILE;
  306.         name = "\p<cannot happen>";
  307.     }
  308.     if (forcedIC)
  309.         ca.ifclient = forcedIC;
  310.     /*
  311.      * figure out what to do with the file/folder
  312.      */
  313.     if (m->f.state > 2)
  314.         todo = m->todo;
  315.     else
  316.     {
  317.         todo = ca.otherwise;
  318.         sp = "\p?E";
  319.         switch (whereisit)
  320.         {
  321.         case xxD:    /* in control file only */
  322.             todo = ca.otherwise;
  323.             sp = "\pE";
  324.             break;
  325.  
  326.         case xSx:    /* on server, but not client */
  327.         case xSD:
  328.             todo = ca.ifserver;
  329.             sp = "\pS";
  330.             if (todo == A_PASS)
  331.             {
  332.                 todo = ca.otherwise;
  333.                 sp = "\pS-E";
  334.             }
  335.             break;
  336.  
  337.         case Cxx:    /* on client, but not server */
  338.         case CxD:
  339.             todo = ca.ifclient;
  340.             sp = "\pC";
  341.             if (todo == A_PASS)
  342.             {
  343.                 todo = ca.otherwise;
  344.                 sp = "\pC-E";
  345.             }
  346.             break;
  347.  
  348.         case CSx:    /* on both client and server */
  349.         case CSD:
  350.             if (ctype == C_FOLDER)
  351.             {
  352.                 todo = ca.otherwise;
  353.                 sp = "\p<E";
  354.                 /*
  355.                  * For a folder, E- means to use the S action instead.
  356.                  */
  357.                 if (todo == A_PASS)
  358.                 {
  359.                     todo = ca.ifserver;
  360.                     sp = "\p<E-S";
  361.                 }
  362.                 break;
  363.             }
  364.             if (serverp->crDate != clientp->crDate && ca.ifcreate != A_PASS)
  365.             {
  366.                 todo = ca.ifcreate;
  367.                 sp = "\pV";
  368.                 break;
  369.             }
  370.             if (serverp->mdDate > clientp->mdDate && ca.ifnewer != A_PASS)
  371.             {
  372.                 todo = ca.ifnewer;
  373.                 sp = "\pN";
  374.                 break;
  375.             }
  376.             if (serverp->mdDate < clientp->mdDate && ca.ifolder != A_PASS)
  377.             {
  378.                 todo = ca.ifolder;
  379.                 sp = "\pO";
  380.                 break;
  381.             }
  382.             if ((serverp->in.f.fileLen != clientp->in.f.fileLen
  383.               || serverp->in.f.rsrcLen != clientp->in.f.rsrcLen)
  384.               && ca.ifsize != A_PASS)
  385.             {
  386.                 todo = ca.ifsize;
  387.                 sp = "\pZ";
  388.                 break;
  389.             }
  390.             todo = ca.otherwise;
  391.             sp = "\pE";
  392.             break;
  393.  
  394.         default:
  395.             todo = A_IGNORE;            /* should not happen */
  396.             sp = "\p??";
  397.         }    /* end of switch on whereisit */
  398.  
  399.         if ((Flags & (PF_VERBOSE|PF_ECHODIST)) == (PF_VERBOSE|PF_ECHODIST))
  400.         {
  401.             StringPtr    s2;
  402.             switch (todo)
  403.             {
  404.             case A_PASS:    s2 = "\p-";    break;
  405.             case A_IGNORE:    s2 = "\pi";    break;
  406.             case A_JUNK:    s2 = "\pj";    break;
  407.             case A_DISCARD:    s2 = "\pd";    break;
  408.             case A_UPDATE:    s2 = "\pu";    break;
  409.             default:        s2 = "\p?";
  410.             }
  411.             notice (L_CRITERION, sp, s2, name, nil);
  412.         }
  413.     }
  414.     /*
  415.      * Now that we know what to do, do it
  416.      */
  417.     switch (todo)
  418.     {
  419.     case A_IGNORE:
  420.     case A_PASS:
  421.         if ((whereisit & xSD) == xxD && ca.ifserver == A_UPDATE)
  422.         {
  423.             sp = distp->altname ? distp->altname : name;
  424.             notice (L_MISSING, sp, nil);
  425.             if (Flags & PF_BADSERVER)
  426.                 panic (false, E_MISSING, sp, nil);
  427.         }
  428.         if (Flags & PF_VERBOSE)
  429.             notice (L_IGNORE, name, nil);
  430.         todo = A_IGNORE;
  431.         break;
  432.  
  433.     case A_JUNK:
  434.         if (!(whereisit & Cxx))
  435.             break;                    /* if it's not on the client, who cares */
  436.         if (m->ij)
  437.             break;                    /* cannot move to junk if already in junk */
  438.         error = moveToJunk (clientp);
  439.         if (error != dupFNErr)
  440.             break;
  441.         notice (L_TOOMANYJ, name, nil);
  442.         todo = A_DISCARD;
  443.         /* fall into ... */
  444.  
  445.     case A_DISCARD:
  446.         if (!(whereisit & Cxx))
  447.             break;                    /* if not on client, can't discard */
  448.         if (ctype == C_FILE)
  449.         {
  450.             error = discard (clientp, true);    /* discard a file */
  451.             break;
  452.         }
  453.         /*
  454.          * Since emptying a folder can take a while, we run it under the
  455.          * dispatcher.
  456.          */
  457.         if (clientp->dirID == File_list[FL_JUNK].f_info.dirID)
  458.             break;                        /* leave the junk folder alone */
  459.         switch (m->f.state)
  460.         {
  461.         case 2:
  462.             paramv[0] = (Ptr) clientp;
  463.             paramv[1] = nil;
  464.             m->f.state = 3;
  465.             m->todo = todo;
  466.             result = pushCall (emptyFolder, paramv);
  467.             if (result != R_CONT)
  468.                 m->f.state = 4;
  469.             else
  470.                 Depth++;
  471.             return result;
  472.         case 3:
  473.             /*
  474.              * After the folder is emptied, we can discard it
  475.              */
  476.             Depth--;
  477.             if (argv == 0 || argv[0] == 0 || *(Boolean *)argv[0] == 0)
  478.                 error = discard (clientp, true);
  479.             m->f.state = 4;
  480.         }
  481.         break;
  482.  
  483.     case A_UPDATE:
  484.         if (!(whereisit & xSx))
  485.         {
  486.             /*
  487.              * Trying to update something which does not have a master
  488.              * copy on the server.  This doesn't make sense for files,
  489.              * but does for folders, since we could perform some
  490.              * action (e.g. junk, discard) on the contents of the
  491.              * folders.
  492.              */
  493.             /*
  494.              * Warn about explicitly mentioned items missing from
  495.              * server, unless the ifclient action indicates this
  496.              * was expected.
  497.              */
  498.             if ((whereisit & xxD) && ca.ifclient != A_UPDATE)
  499.             {
  500.                 notice (L_MISSING, name, nil);
  501.             }
  502.             if (ctype == C_FILE || whereisit == xxD)
  503.             {
  504.                 todo = A_IGNORE;
  505.                 break;
  506.             }
  507.         }
  508.         /*
  509.          * If we are updating a file, use copyFile.
  510.          * To update a folder, use ourselves.
  511.          */
  512.         if (ctype == C_FILE)
  513.         {
  514.             switch (m->f.state)
  515.             {
  516.             case 2:
  517.                 /*
  518.                  * Possibly make backup by moving client to junk
  519.                  */
  520.                 if (ca.backup == A_SET && (whereisit & Cxx) && !m->ij)
  521.                 {
  522.                     error = moveToJunk (clientp);
  523.                     if (error == dupFNErr)
  524.                         notice (L_TOOMANYJ, name, nil);
  525.                     if (error == 0)
  526.                     {
  527.                         /*
  528.                          * If we moved it, pretend it did not exist on client
  529.                          */
  530.                         whereisit &= ~Cxx;
  531.                         wherereal &= ~Cxx;
  532.                         m->clientp = m->clientp->link;
  533.                     }
  534.                     error = 0;
  535.                 }
  536.                 if (!(whereisit & Cxx))
  537.                 {
  538.                     /*
  539.                      * If file not already on client, build a dummy catalog
  540.                      * entry for it
  541.                      */
  542.                     clientp = &m->tcn;
  543.                     ZEROAT (clientp);
  544.                     clientp->ctype = ctype;
  545.                     clientp->parID = m->cd;
  546.                     COPYPS (name, clientp->name);
  547.                 }
  548.                 paramv[0] = (Ptr) serverp;
  549.                 paramv[1] = (Ptr) clientp;
  550.                 paramv[2] = (Ptr) m->name;
  551.                 m->f.state = 3;
  552.                 m->todo = todo;
  553.                 result = pushCall (copyFile, paramv);
  554.                 if (result != R_CONT)
  555.                     m->f.state = 4;
  556.                 return result;
  557.  
  558.             case 3:
  559.                 m->f.state = 4;
  560.                 error = 0;
  561.                 if (argv && argv[0])
  562.                     uerror = *(OSErr *)(argv[0]);
  563.                 if (!(whereisit & Cxx))
  564.                 {
  565.                     clientp = &m->tcn;
  566.                     whereisit |= Cxx;
  567.                 }
  568.             }
  569.             break;
  570.         }
  571.         /*
  572.          * Here for folder update
  573.          */
  574.         switch (m->f.state)
  575.         {
  576.         case 2:
  577.             /*
  578.              * If the folder is not in the control list, use the dummy
  579.              * dnode to describe it while it is being updated.
  580.              */
  581.             if (! (whereisit & xxD) )
  582.             {
  583.                 distp = &m->tdn;
  584.                 ZEROAT (distp);
  585.                 COPYPS (name, distp->name);
  586.                 if (m->dt)                /* propagate type list */
  587.                     distp->tlistp = m->dt->tlistp;
  588.             }
  589.             else
  590.                 if (distp && distp->childp)
  591.                     mergeActions (&m->da, &distp->childp->actions, &ca);
  592.             /*
  593.              * If the folder does not exist yet on client, create it.
  594.              */
  595.             if (! (whereisit & Cxx))
  596.             {
  597.                 clientp = &m->tcn;
  598.                 if (Flags & PF_LISTONLY)
  599.                 {
  600.                     notice (L_WCREATE, name, nil);
  601.                     ZEROAT (clientp);
  602.                     clientp->ctype = ctype;
  603.                     COPYPS(name, clientp->name);
  604.                 }
  605.                 else
  606.                 {
  607.                     error = createFolder (name, ClientVol, m->cd, serverp);
  608.                     if (error == 0)
  609.                         error = getInfo (name, ClientVol, m->cd, clientp);
  610.                     if (error)
  611.                     {
  612.                         uerror = error;
  613.                         warning (E_SYS, nil);
  614.                         error = 0;
  615.                         break;
  616.                     }
  617.                 }
  618.                 whereisit |= Cxx;
  619.             }
  620.             if (clientp->attrib & fLocked)
  621.                 (void) unlock (clientp);
  622.             paramv[0] = (Ptr) distp;
  623.             paramv[1] = (Ptr) &ca;
  624.             if (whereisit & xSx)
  625.                 paramv[2] = (Ptr) &serverp->dirID;
  626.             else
  627.             {
  628.                 temp = 0;
  629.                 paramv[2] = (Ptr) &temp;
  630.             }
  631.             paramv[3] = (Ptr) &clientp->dirID;
  632.             paramv[4] = (Ptr) &m->ij;
  633.             m->f.state = 3;
  634.             m->todo = todo;
  635.             result = pushCall (matchFolder, paramv);
  636.             if (result != R_CONT)
  637.                 m->f.state = 4;
  638.             else
  639.                 Depth++;
  640.             return result;
  641.         case 3:
  642.             m->f.state = 4;
  643.             Depth--;
  644.             if (!(whereisit & Cxx))
  645.             {
  646.                 clientp = &m->tcn;
  647.                 whereisit |= Cxx;
  648.             }
  649.             if (serverp && (serverp->attrib & fLocked) &&
  650.                     !(Flags&PF_LISTONLY))
  651.                 (void) relock (clientp);
  652.             break;
  653.         }
  654.         break;
  655.  
  656.     default:
  657.         notice (L_NOWHAT, name, nil);
  658.         break;
  659.     } /* end of todo switch */
  660.     /*
  661.      * Update alias target, if requested
  662.      */
  663.     if ((todo == A_UPDATE) && error == 0 && uerror == 0
  664.     && (whereisit & Cxx)
  665.     && (clientp->in.f.finfo.fdFlags & fIsAlias)
  666.     && clientp->ctype == C_FILE
  667.     && ca.ifsize == A_PASS && ca.ifolder == A_PASS)
  668.     {
  669.         error = update_alias (clientp);
  670.         if (error > 0)
  671.             error = 0;
  672.         if (error)
  673.         {
  674.             ClueID = error;
  675.             warning (E_SYS, nil);
  676.         }
  677.     }
  678.     if (error == fBsyErr)
  679.         error = 0;                    /* ignore busy file: it may be us
  680.                                      * or we may be running under
  681.                                      * MultiFinder */
  682.     /*
  683.      * If updated special file, set restart flag
  684.      */
  685.     if (todo == A_UPDATE && ca.reboot == A_SET
  686.     &&  (whereisit & Cxx)
  687.     &&  clientp->ctype == C_FILE
  688.     &&  error == 0 && uerror == 0)
  689.     {
  690.         if (!(Flags & PF_RESTART))
  691.         {
  692.             Flags |= PF_RESTART;
  693.             setStat ();
  694.         }
  695.     }
  696.     /*
  697.      * Update Finder information, if requested.
  698.      */
  699.     if ((todo == A_UPDATE || todo == A_IGNORE)
  700.     &&  error == 0 && uerror == 0)
  701.     {
  702.         if ((whereisit & CSx) == CSx
  703.         && (serverp->attrib & fProtect)
  704.         && !(clientp->attrib & fProtect)
  705.         && !(clientp->in.f.finfo.fdFlags & fInvisible) )
  706.         {
  707.             notice (L_PROTECT, name, nil);
  708.             ca.invisible = A_SET;
  709.         }
  710.         if (ca.copywindow == A_UPDATE || ca.invisible >= A_SET
  711.          || ca.locked >= A_SET)
  712.             updateFInfo (clientp, serverp, whereisit,
  713.                     ca.copywindow, ca.invisible, ca.locked);
  714.     }
  715.     if (wherereal & xxD)
  716.         m->distp = m->distp->sibp;
  717.     if (wherereal & xSx)
  718.         m->serverp = m->serverp->link;
  719.     if (wherereal & Cxx)
  720.         m->clientp = m->clientp->link;
  721.     m->f.state = 2;
  722.     if ((error || Quit) && result < R_BACKOUT)
  723.         result = R_BACKOUT;
  724.     return result;
  725.  
  726. cleanexit:
  727.     if (GetHandleSize ((Handle)fh) >= sizeof (lm_t))
  728.     {
  729.         freeList (m->ct);
  730.         freeList (m->st);
  731.     }
  732.     return (popCall (result, nil));
  733. }
  734.  
  735.  
  736.  
  737. /*
  738.  *=========================================================================
  739.  * checkTypes (dp, cp) - find type_node applying to file
  740.  * entry:    dp = ptr to dnode of folder containing file
  741.  *            cp = ptr to catalog node describing file
  742.  * exit:    returns ptr to type_list which applies (possibly nil)
  743.  *=========================================================================
  744.  */
  745.  
  746. tnode_t *
  747. checkTypes (dp, cp)
  748.     dnode_t    *        dp;
  749.     cnode_t *        cp;
  750. {
  751. register OSType        ft;                /* file type */
  752. register OSType        fc;                /* file creator */
  753. register tnode_t *    tp;                /* type_node ptr */
  754.  
  755.     tp = dp->tlistp;
  756.     if (cp == nil || tp == nil)
  757.         return nil;
  758.     ft = cp->in.f.finfo.fdType;
  759.     fc = cp->in.f.finfo.fdCreator;
  760.     if (cp->in.f.finfo.fdFlags & fIsAlias)
  761.     {
  762.         ft = AliasType;
  763.         fc = AliasCreator;
  764.     }
  765.     if (ft == 0 && EqualString (cp->name, IconFName, false, false))
  766.     {
  767.         ft = IconType;
  768.         fc = IconCreator;
  769.     }
  770.     if (ft == 0)
  771.         ft = '????';
  772.     if (fc == 0)
  773.         fc = '????';
  774.     for (; tp; tp = tp->morep)
  775.     {
  776.         if (tp->ftype && tp->ftype != ft)
  777.             continue;                /* mismatch on type */
  778.         if (tp->fcreator && tp->fcreator != fc)
  779.             continue;                /* mismatch on creator */
  780.         break;
  781.     }
  782.     return tp;
  783. }
  784.  
  785.  
  786.  
  787.  
  788. /*
  789.  *=========================================================================
  790.  * least (c, s, d) - find alphabetical least of three strings
  791.  * entry:    c = client name string
  792.  *            s = server name string
  793.  *            d = control file name string
  794.  * returns:    bit pattern indicating which of the sources has the least string
  795.  *=========================================================================
  796.  */
  797. short
  798. least (c, s, d)
  799. register StringPtr c, s, d;
  800. {
  801.     switch (RelString (c, s, false, true))
  802.     {
  803.     case -1:                        /* C < S, find lesser of C and D */
  804.         switch (RelString (c, d, false, true))
  805.         {
  806.         case -1:    return Cxx;
  807.         case 0:        return CxD;
  808.         case 1:        return xxD;
  809.         }
  810.     case 0:                            /* C == S, how about D */
  811.         switch (RelString (c, d, false, true))
  812.         {
  813.         case -1:    return CSx;
  814.         case 0:        return CSD;
  815.         case 1:        return xxD;
  816.         }
  817.     case 1:                            /* S < C, find lesser of S and D */
  818.         switch (RelString (s, d, false, true))
  819.         {
  820.         case -1:    return xSx;
  821.         case 0:        return xSD;
  822.         case 1:        return xxD;
  823.         }
  824.     }
  825.     /*NOTREACHED*/
  826.     return 0;
  827. }
  828.  
  829.  
  830.  
  831. /*
  832.  *=========================================================================
  833.  * mergeActions (def, src, dst) - merge default and specific action lists
  834.  * entry:    def = pointer to default action list
  835.  *            src = pointer to specific action list
  836.  *            dst = pointer to list for result of merge
  837.  *            def and dst can be the same without confusion
  838.  *=========================================================================
  839.  */
  840. void
  841. mergeActions (def, src, dst)
  842. register actions_t *    def;
  843. register actions_t *    src;
  844. register actions_t *    dst;
  845. {
  846. register action_t        a;
  847.  
  848.     /*
  849.      * Easy.  Just copy the src to the dst, supplying values from def
  850.      * whereever src action is A_DEFAULT
  851.      */
  852.     dst->ifclient    = (a = src->ifclient  ) != A_DEFAULT ? a: def->ifclient;
  853.     dst->ifserver    = (a = src->ifserver  ) != A_DEFAULT ? a: def->ifserver;
  854.     dst->ifcreate    = (a = src->ifcreate  )    != A_DEFAULT ? a: def->ifcreate;
  855.     dst->ifnewer    = (a = src->ifnewer      ) != A_DEFAULT ? a: def->ifnewer;
  856.     dst->ifolder    = (a = src->ifolder      ) != A_DEFAULT ? a: def->ifolder;
  857.     dst->ifsize        = (a = src->ifsize      ) != A_DEFAULT ? a: def->ifsize;
  858.     dst->otherwise    = (a = src->otherwise ) != A_DEFAULT ? a: def->otherwise;
  859.     dst->copywindow    = (a = src->copywindow)    != A_DEFAULT ? a: def->copywindow;
  860.     dst->invisible    = (a = src->invisible ) != A_DEFAULT ? a: def->invisible;
  861.     dst->locked        = (a = src->locked      ) != A_DEFAULT ? a: def->locked;
  862.     dst->backup        = (a = src->backup      ) != A_DEFAULT ? a: def->backup;
  863.     dst->reboot        = (a = src->reboot      ) != A_DEFAULT ? a: def->reboot;
  864. }
  865.  
  866.  
  867. /*
  868.  *=========================================================================
  869.  * updateFInfo (cp, sp, where, window, hide) - change Finder info for client
  870.  *            file/folder
  871.  * entry:    cp = pointer to client catalog node
  872.  *            sp = pointer to server catalog node
  873.  *            where = bits for which of cp and sp are valid
  874.  *            window = A_UPDATE to copy server info to client
  875.  *            hide = A_SET to make invisible, A_DISCARD to make visible,
  876.  *                    or A_UPDATE to copy server to client
  877.  *            lock = same as hide, but for locked attribute
  878.  * Since this is just a frill, errors are silently ignored
  879.  *=========================================================================
  880.  */
  881. static
  882. void
  883. updateFInfo (cp, sp, where, window, hide, lock)
  884. register cnode_t *        cp;
  885. register cnode_t *        sp;
  886.     short                where;
  887.     action_t            window;
  888.     action_t            hide;
  889.     action_t            lock;
  890. {
  891.     int                    doit;        /* non-zero if need to change client */
  892.     OSErr                error;
  893. register long *            cl, *sl;    /* used to speed compares */
  894.     int                    have;        /* current client visibility or lock */
  895.     Integer                temp;        /* temp to hold client file folder info */
  896.     int                    wantl;        /* lockedness wanted on client */
  897.     int                    wantv;        /* visibility wanted on client */
  898.     CInfoPBRec            ci;
  899.  
  900.     if ((where & Cxx) == 0 || cp == 0)
  901.         return;                        /* if not on client, can't do anything */
  902.     doit = 0;
  903.     /*
  904.      * Check if need to change visibility
  905.      */
  906.     have = cp->in.f.finfo.fdFlags & fInvisible;
  907.     switch (hide)
  908.     {
  909.     case A_SET:        wantv = fInvisible; break;
  910.     case A_DISCARD:    wantv = 0; break;
  911.     case A_UPDATE:    if (sp)
  912.                         { wantv = sp->in.f.finfo.fdFlags & fInvisible; break; }
  913.     default:        wantv = have;
  914.     }
  915.     if (have != wantv)
  916.         doit |= 2;
  917.     /*
  918.      * If we should update the window info from the server and the
  919.      * server exists and the info differs between the client and server,
  920.      * then we need to do the update.
  921.      */
  922.     if ((where & xSx) && sp && window == A_UPDATE)
  923.     {
  924.         cp->in.f.finfo.fdFlags &= ~fInvisible;
  925.         temp = sp->in.f.finfo.fdFlags;
  926.         sp->in.f.finfo.fdFlags &= ~fInvisible;
  927.         switch (cp->ctype)
  928.         {
  929.         case C_FILE:
  930.             if (cp->in.f.finfo.fdType        != sp->in.f.finfo.fdType
  931.              || cp->in.f.finfo.fdCreator    != sp->in.f.finfo.fdCreator
  932.              || cp->in.f.finfo.fdFlags        != sp->in.f.finfo.fdFlags
  933.              || *(long *)&cp->in.f.finfo.fdLocation !=
  934.                 *(long *)&sp->in.f.finfo.fdLocation
  935.             )
  936.                 doit = 1;
  937.             break;
  938.         case C_FOLDER:
  939.             cl = (long *)&cp->in.d.dinfo;
  940.             sl = (long *)&sp->in.d.dinfo;
  941.             if (cl[0] != sl[0]            /* frRect top, left */
  942.              || cl[1] != sl[1]            /* frRect bottom, right */
  943.              || cl[2] != sl[2]            /* frFlags & frLocation.v */
  944.              || cl[3] != sl[3]            /* frLocation.h & frView */
  945.              || cp->in.d.frScroll.v != sp->in.d.frScroll.v
  946.              || cp->in.d.frScroll.h != sp->in.d.frScroll.h
  947.              || (cp->in.d.frOChain != 0 && sp->in.d.frOChain == 0)
  948.             )
  949.                 doit = 1;
  950.             break;
  951.         }
  952.     }
  953.     /*
  954.      * Now check lock/unlock
  955.      */
  956.     have = cp->attrib & fLocked;
  957.     switch (lock)
  958.     {
  959.     case A_SET:        wantl = fLocked; break;
  960.     case A_DISCARD:    wantl = 0; break;
  961.     case A_UPDATE:    if (sp)
  962.                         { wantl = sp->attrib & fLocked; break; }
  963.     default:        wantl = have;
  964.     }
  965.     if (have != wantl)
  966.         doit |= 4;
  967.     /*
  968.      * If we need to change client's info, get the current values again,
  969.      * change them, and set them back.
  970.      */
  971.     if (doit)
  972.     {
  973.         ZERO (ci);
  974.         ci.hFileInfo.ioVRefNum = ClientVol;
  975.         if (cp->ctype == C_FILE)
  976.         {
  977.             ci.hFileInfo.ioNamePtr = cp->name;
  978.             ci.hFileInfo.ioDirID = cp->parID;
  979.             ci.hFileInfo.ioFDirIndex = 0;
  980.         }
  981.         else
  982.         {
  983.             ci.hFileInfo.ioDirID = cp->dirID;
  984.             ci.hFileInfo.ioFDirIndex = -1;
  985.         }
  986.         error = PBGetCatInfo (&ci, false);
  987.         if (error)
  988.             return;
  989.         if (doit & 1)
  990.         {
  991.             if (cp->ctype == C_FILE)
  992.             {
  993.                 temp = ci.hFileInfo.ioFlFndrInfo.fdFldr;
  994.                 ci.hFileInfo.ioFlFndrInfo = sp->in.f.finfo;
  995.                 ci.hFileInfo.ioFlFndrInfo.fdFldr = temp;
  996.             }
  997.             else
  998.             {
  999.                 ci.dirInfo.ioDrUsrWds = sp->in.d.dinfo;
  1000.                 ci.dirInfo.ioDrFndrInfo.frScroll = sp->in.d.frScroll;
  1001.                 if (sp->in.d.frOChain == 0)
  1002.                     ci.dirInfo.ioDrFndrInfo.frOpenChain = 0;
  1003.             }
  1004.         }
  1005.         ci.hFileInfo.ioFlFndrInfo.fdFlags =
  1006.             (ci.hFileInfo.ioFlFndrInfo.fdFlags & ~fInvisible) | wantv;
  1007.         ci.hFileInfo.ioFlAttrib =
  1008.             (ci.hFileInfo.ioFlAttrib & ~fLocked) | wantl;
  1009.         if (Flags & PF_VERBOSE)
  1010.             notice (L_FINFO, cp->name, nil);
  1011.         if (Flags & PF_LISTONLY)
  1012.             return;
  1013.         if (cp->ctype == C_FILE)
  1014.         {
  1015.             ci.hFileInfo.ioDirID = cp->parID;
  1016.             ci.hFileInfo.ioFDirIndex = 0;
  1017.         }
  1018.         error = PBSetCatInfo (&ci, false);
  1019.         if (error)
  1020.         {
  1021.             ClueID = error;
  1022.             notice (L_SYS, "\pUpdateFInfo", "\pPBSetCatInfo",nil);
  1023.         }
  1024.         /*
  1025.          * Since changing the fLocked bit in the attributes doesn't really
  1026.          * lock or unlock anything (Why?), we may need to explicitly lock
  1027.          * or relock the file/folder.
  1028.          */
  1029.         if (doit & 4)
  1030.         {
  1031.             if (wantl)
  1032.                 relock (cp);
  1033.             else
  1034.                 unlock (cp);
  1035.         }
  1036.     }
  1037. }
  1038.  
  1039.  
  1040. /*
  1041.  *=========================================================================
  1042.  * updateRoot (dp) - update finder window information for client root folder
  1043.  * entry:    dp = pointer to dist_node for client root folder
  1044.  * technique:    just set up can call updateFInfo ()
  1045.  *=========================================================================
  1046.  */
  1047. void
  1048. updateRoot (dp)
  1049.     dnode_t            *dp;
  1050. {
  1051.     cnode_t            *clientp, *serverp;    /* args for updateFInfo () */
  1052.     actions_t        *ra;                /* action list for root */
  1053.     file_info_t        *fi;                /* ptr into File_list[] */
  1054.  
  1055.     /*
  1056.      * Exit if nothing to do.
  1057.      * Note that most of these exit conditions should never happen!
  1058.      */
  1059.     if (! dp)
  1060.         return;
  1061.     fi = &File_list[FL_ROOT];
  1062.     if (! fi->f_set)
  1063.         return;
  1064.     clientp = &fi->f_info;
  1065.     fi = &File_list[FL_MAST];
  1066.     if (! fi->f_set)
  1067.         return;
  1068.     serverp = &fi->f_info;
  1069.     ra = &dp->actions;
  1070.     if (ra->copywindow == A_UPDATE
  1071.      || ra->invisible >= A_SET
  1072.      || ra->locked >= A_SET)
  1073.     {
  1074.         updateFInfo (clientp, serverp, CSD,
  1075.                         ra->copywindow, ra->invisible, ra->locked);
  1076.     }
  1077. }